package com.jbp.service;
import com.jbp.model.Gift;
import com.jbp.model.GiftLot;
import com.jbp.redis.RedisConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**爆率版抽奖工具*/
@Service
public class LuckyUtil3 {
    @Autowired
    private RedisConfigService redisConfigService;

    @Autowired
    private PropertyService propertyService;

    public List<Gift> luck(Integer userId, int times, List<GiftLot> list, BigDecimal bloodLine) {

        //    //总爆率
        double winRate = Double.parseDouble(propertyService.getV("luck_win_rate_sys"));
//    //个人最高爆率
        double personRateHigh = Double.parseDouble(propertyService.getV("luck_high_usr"));
        //奖池礼物数量
        int poolSize = Integer.parseInt(propertyService.getV("pool_size"));
        int minPoolSize = Integer.parseInt(propertyService.getV("min_pool_size"));
        int maxBlood = Integer.parseInt(propertyService.getV("max_blood"));
        double bloodRate = Double.parseDouble(propertyService.getV("blood_rate"));
        double bloodBase = Double.parseDouble(propertyService.getV("blood_base"));

        bloodBase = bloodLine==null || bloodLine.doubleValue()==0 ? bloodBase : bloodLine.doubleValue();

        //系统总产出
        int allIncome = 0;
        Integer r_allIncome = (Integer) redisConfigService.get("haoya:luck:allincome");
        if (r_allIncome!=null) {
            allIncome = r_allIncome;
        }
        //系统总投入
        int allPay = 0;
        Integer r_allPay = (Integer) redisConfigService.get("haoya:luck:allpay");
        if (r_allPay!=null) {
            allPay = r_allPay;
        }
        //当前系统爆率
        double totalRate = 1.1;
        Double r_totalRate = (Double) redisConfigService.get("haoya:luck:totalRate");
        if (r_totalRate!=null) {
            totalRate = r_totalRate;
        }

        //个人投入
        int pay = 0;
        Integer r_pay = (Integer) redisConfigService.get("haoya:luck:upay:"+userId);
        if (r_pay!=null) {
            pay = r_pay;
        }
        //个人产出
        int income = 0;
        Integer r_income = (Integer) redisConfigService.get("haoya:luck:uincome:"+userId);
        if (r_income!=null) {
            income = r_income;
        }

        //水池
        int pool = 0;
        Integer r_pool = (Integer) redisConfigService.get("haoya:luck:pool");
        if (r_pool!=null) {
            pool = r_pool;
        }

        //奖池剩余礼物数量
        int giftCount = 0;
        Integer r_giftCount = (Integer) redisConfigService.get("haoya:luck:giftcount");
        if (r_giftCount!=null) {
            giftCount = r_giftCount;
        }

        //生成奖池
        if (giftCount<=minPoolSize) {
            giftCount += poolSize;
            pool += poolSize * winRate * 20;
            System.out.println("生成奖池："+pool);
        }

        List<Gift> result = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            giftCount --;
            pay += 20;
            allPay += 20;

            //剩余爆率， 根据剩余爆率控制用户爆率在1.2到personRateHigh之间
            double rate = BigDecimal.valueOf(pool).divide(BigDecimal.valueOf(giftCount*20), 4, BigDecimal.ROUND_HALF_UP).doubleValue();

            personRateHigh = Math.min(Math.max(3*rate*rate, 1.18), personRateHigh);

            //用户最大奖限制
            int biggest = (int) Math.min(pool/3, personRateHigh * pay - income);

            if (rate<0.48) {
                biggest = Math.min(biggest, 520);
            } else if (rate<0.68) {
                biggest = Math.min(biggest, 999);
            } else if (rate<0.86) {
                biggest = Math.min(biggest, 2333);
            } else if (rate<1.02) {
                biggest = Math.min(biggest, 5200);
            } else if (rate<1.08) {
                biggest = Math.min(biggest, 10001);
            } else if (rate<1.11) {
                biggest = Math.min(biggest, 18888);
            } else if (rate<1.13) {
                biggest = Math.min(biggest, 33440);
            }

            biggest = Math.min(biggest, 52000);
            biggest = Math.max(biggest, 52);

            //血
            int blood = Math.min(maxBlood, Math.max((int) ((bloodBase * pay - income)*bloodRate), 0));

            //抽奖
            Gift gift = luck(biggest, list, blood);
            income += gift.getCoin();
            allIncome += gift.getCoin();
            pool -= gift.getCoin();

            result.add(gift);
        }

        redisConfigService.set("haoya:luck:allincome", allIncome);
        redisConfigService.set("haoya:luck:allpay", allPay);
        redisConfigService.set("haoya:luck:totalRate", totalRate);
        redisConfigService.set("haoya:luck:pool", pool);
        redisConfigService.set("haoya:luck:giftcount", giftCount);
        redisConfigService.set("haoya:luck:upay:"+userId, pay);
        redisConfigService.set("haoya:luck:uincome:"+userId, income);
        return result;
    }

    private Gift luck(double biggest, List<GiftLot> giftLots, int blood) {
        List<GiftLot> pool = giftLots.stream().filter(giftLot -> giftLot.getGift().getCoin() <= biggest)
                .collect(Collectors.toList());

        int sum = pool.stream().mapToInt(giftLot->giftLot.getGiftNum()).sum();
        int i = new Random().nextInt(sum-blood)+blood;
        int t = 0;
        for (GiftLot giftLot : pool) {
            t += giftLot.getGiftNum();
            if (i<=t) {
                return giftLot.getGift();
            }
        }
        throw new RuntimeException("抽奖错误");
    }

    public List<Gift> luckTora(Integer userId, int times, List<GiftLot> list) {
        //    //总爆率
        double winRate = 1.17;
//    //个人最高爆率
        double personRateHigh = 1.50;

        //个人投入
        int pay = 0;
        Integer r_pay = (Integer) redisConfigService.get("haoya:luck:upay:"+userId);
        if (r_pay!=null) {
            pay = r_pay;
        }

        //个人产出
        int income = 0;
        Integer r_income = (Integer) redisConfigService.get("haoya:luck:uincome:"+userId);
        if (r_income!=null) {
            income = r_income;
        }

        List<Gift> result = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            pay += 20;
            //用户最大奖限制
            int biggest = (int) Math.min(winRate * pay - income, personRateHigh * pay - income);
            biggest = Math.max(biggest, 52);

            //血
            int blood = Math.min(100000, Math.max((int) (winRate * pay - income)*2, 0));

            //抽奖
            Gift gift = luck(biggest, list, blood);
            income += gift.getCoin();
            result.add(gift);
        }
        redisConfigService.set("haoya:luck:upay:"+userId, pay);
        redisConfigService.set("haoya:luck:uincome:"+userId, income);
        return result;
    }

    //打印统计
    private static void printTj(ArrayList<Double> bls , int times){
        System.out.println("总计抽奖:"+bls.size()+"回， 每回"+times+"次");
        System.out.println("爆率<30:"+bls.stream().filter(bl->bl<30).count()+"次");
        System.out.println("30<=爆率<40:"+bls.stream().filter(bl->bl>=30 && bl<40).count()+"次");
        System.out.println("40<=爆率<50:"+bls.stream().filter(bl->bl>=40 && bl<50).count()+"次");
        System.out.println("50<=爆率<60:"+bls.stream().filter(bl->bl>=50 && bl<60).count()+"次");
        System.out.println("60<=爆率<70:"+bls.stream().filter(bl->bl>=60 && bl<70).count()+"次");
        System.out.println("70<=爆率<80:"+bls.stream().filter(bl->bl>=70 && bl<80).count()+"次");
        System.out.println("80<=爆率<90:"+bls.stream().filter(bl->bl>=80 && bl<90).count()+"次");
        System.out.println("90<=爆率<100:"+bls.stream().filter(bl->bl>=90 && bl<100).count()+"次");
        System.out.println("100<=爆率<110:"+bls.stream().filter(bl->bl>=100 && bl<110).count()+"次");
        System.out.println("110<=爆率<120:"+bls.stream().filter(bl->bl>=110 && bl<120).count()+"次");
        System.out.println("120<=爆率<130:"+bls.stream().filter(bl->bl>=120 && bl<130).count()+"次");
        System.out.println("130<=爆率<140:"+bls.stream().filter(bl->bl>=130 && bl<140).count()+"次");
        System.out.println("140<=爆率<150:"+bls.stream().filter(bl->bl>=140 && bl<150).count()+"次");
        System.out.println("爆率>=150:"+bls.stream().filter(bl->bl>=150).count()+"次");
    }
    //打印爆率
    private static void printBl(ArrayDeque<Integer> gifts, int times){
        while (gifts.size()>=times) {
            List<Integer> lucks = new ArrayList<>();
            for (int i = 0; i < times; i++) {
                lucks.add(gifts.pop());
            }
            int cc = lucks.stream().mapToInt((i) -> i).sum();
            BigDecimal bl = BigDecimal.valueOf(cc * 100).divide(BigDecimal.valueOf(times * 20), 4, BigDecimal.ROUND_HALF_UP);

            System.out.println("抽奖"+times+"次 投入:"+times*20+" 产出："+cc + "  爆率:"+bl);
        }
    }

    private static void printConfig(Map<Integer, Integer> map) {
        //总价值
        long totalCoin = map.entrySet().stream().mapToInt(e -> e.getKey()*e.getValue()).sum();
        //总数量
        long totalSize = map.values().stream().mapToInt(v->v).sum();
        double zbl = BigDecimal.valueOf(totalCoin).divide(BigDecimal.valueOf(totalSize*20), 6, BigDecimal.ROUND_HALF_UP).doubleValue();

        for (Map.Entry<Integer, Integer> e : map.entrySet()) {
            double bl = BigDecimal.valueOf(e.getValue() * 100).divide(BigDecimal.valueOf(totalSize), 6, BigDecimal.ROUND_HALF_UP).doubleValue();
            double zb = BigDecimal.valueOf(e.getValue() * e.getKey() * 100).divide(BigDecimal.valueOf(totalCoin), 6, BigDecimal.ROUND_HALF_UP).doubleValue();

            System.out.println(e.getKey()+"钻  "+ e.getValue()+"个   "+" 爆率:"+ bl +"    占比:"+zb);
        }
        System.out.println("总数量："+totalSize+"  总价值:"+totalCoin+"  总爆率："+zbl);
    }


    private int luckt(double biggest, Map <Integer, Integer> sorce, int blood) {
        Map <Integer, Integer> pool = new HashMap<>();
        sorce.keySet().stream().filter(i -> i<=biggest).forEach(k->{
            pool.put(k, sorce.get(k));
        });
        int sum = pool.values().stream().mapToInt(Integer::intValue).sum();
//        System.out.println("sum:"+sum+" blood:"+blood+"  big:"+biggest);
        int i = new Random().nextInt(sum-blood)+blood;
        int t = 0;
        for (Map.Entry<Integer, Integer> entry : pool.entrySet()) {
            t += entry.getValue();
            if (i<=t) {
                return entry.getKey();
            }
        }
        throw new RuntimeException("抽奖错误");
    }
    //系统总产出
    int allIncome = 0;
    //系统总投入
    int allPay = 0;
    //水池
    int pool = 0;
    //奖池剩余礼物数量
    int giftCount = 0;
    int giftSize = 50000;
    BigDecimal luckTest(UserLuck userLuck, Map <Integer, Integer> out) {
        //总爆率
        double winRate = 1.14;
        //个人最高爆率
        double personRateHigh = 5;
//        //个人投入
//        int pay = 0;
//        //个人产出
//        int income = 0;

        Map <Integer, Integer> map = getGiftCoinfig();
        ArrayList<Integer> lucks = new ArrayList<>();

        if (pool>0 && userLuck.pay==0) {
            double rate1 = BigDecimal.valueOf(pool).divide(BigDecimal.valueOf(giftCount*20), 4, BigDecimal.ROUND_HALF_UP).doubleValue();
            System.out.println("剩余礼物:"+giftCount+"    水池:"+pool +"   爆率："+rate1);
//            try {
//                Thread.sleep(10);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }
        for (int i = 0; i < 1; i++) {
            //生成奖池
            if (giftCount<=2000) {
                giftCount += giftSize;
                pool += giftSize * winRate * 20;
                System.out.println("生成奖池："+pool);
            }

            giftCount --;
            userLuck.pay += 20;
            allPay +=20;


            //剩余爆率
            double rate = BigDecimal.valueOf(pool).divide(BigDecimal.valueOf(giftCount*20), 4, BigDecimal.ROUND_HALF_UP).doubleValue();

            //personRateHigh = Math.min(Math.max(3*rate*rate, 1.18), personRateHigh);

            //用户最大奖限制
            int biggest = (int) Math.min(pool/3, personRateHigh * userLuck.pay - userLuck.income);

            if (rate<0.48) {
                biggest = Math.min(biggest, 520);
            } else if (rate<0.68) {
                biggest = Math.min(biggest, 999);
            } else if (rate<0.86) {
                biggest = Math.min(biggest, 2333);
            } else if (rate<1.02) {
                biggest = Math.min(biggest, 5200);
            } else if (rate<1.08) {
                biggest = Math.min(biggest, 10001);
            } else if (rate<1.11) {
                biggest = Math.min(biggest, 18888);
            } else if (rate<1.13) {
                biggest = Math.min(biggest, 33440);
            }

            biggest = Math.min(biggest, 52000);
            biggest = Math.max(biggest, 52);

            //血
            int blood = Math.min(100000, Math.max((int) ((userLuck.bloodBase * userLuck.pay - userLuck.income)*3), 0));
//            blood = 0;

            //抽奖
            int luck = luckt(biggest, map, blood);
            lucks.add(luck);
            userLuck.income += luck;
            allIncome += luck;
            pool -= luck;

            if (out.containsKey(luck)) {
                out.put(luck, out.get(luck)+1);
            } else {
                out.put(luck, 1);
            }
        }

        BigDecimal rate = BigDecimal.valueOf(userLuck.income).divide(BigDecimal.valueOf(userLuck.pay), 2, BigDecimal.ROUND_HALF_UP);
        BigDecimal allRate = BigDecimal.valueOf(allIncome).divide(BigDecimal.valueOf(allPay), 4, BigDecimal.ROUND_HALF_UP);
//        System.out.println("抽奖：" +1+"次，爆率:"+rate +" 总爆率："+allRate + "  水池："+pool);
        return rate;
    }

    private static Map <Integer, Integer> getGiftCoinfig(){
        Map <Integer, Integer> map = new LinkedHashMap<>();
        /*map.put(1, 168634);
        map.put(5, 50000);
        map.put(20, 25000);
        map.put(52, 1200);
        map.put(100, 500);
        map.put(200, 500);
        map.put(520, 700);
        map.put(999, 420);
        map.put(2333, 210);
        map.put(5200, 105);
        map.put(10001, 61);
        map.put(18888, 35);
        map.put(33440, 22);
        map.put(52000, 14);*/
        /*map.put(1, 168634);
        map.put(5, 50000);
        map.put(20, 25000);
        map.put(52, 1200);
        map.put(100, 500);
        map.put(200, 500);
        map.put(520, 990);
        map.put(999, 700);
        map.put(2333, 350);
        map.put(5200, 151);
        map.put(10001, 56);
        map.put(18888, 24);
        map.put(33440, 13);
        map.put(52000, 6);*/
        /*map.put(1, 168634);
        map.put(5, 50000);
        map.put(20, 25000);
        map.put(52, 2800);
        map.put(100, 1000);
        map.put(200, 600);
        map.put(520, 1600);
        map.put(999, 750);
        map.put(2333, 320);
        map.put(5200, 151);
        map.put(10001, 56);
        map.put(18888, 16);
        map.put(33440, 9);
        map.put(52000, 4);*/
        map.put(1, 116833);
        map.put(5, 19999);
        map.put(20, 5001);
        map.put(52, 3691);
        map.put(100, 1550);
        map.put(200, 780);
        map.put(520, 1200);
        map.put(999, 600);
        map.put(2333, 235);
        map.put(5200, 85);
        map.put(10001, 19);
        map.put(18888, 3);
        map.put(33440, 2);
        map.put(52000, 1);
        /*map.put(1, 121154);
        map.put(10, 18175);
        map.put(20, 4000);
        map.put(52, 3000);
        map.put(99, 1000);
        map.put(199, 887);
        map.put(520, 894);
        map.put(1000, 400);
        map.put(1888, 280);
        map.put(3344, 100);
        map.put(5200, 75);
        map.put(11111, 29);
        map.put(20200, 3);
        map.put(33440, 2);
        map.put(52000, 1);*/
        return map;
    }

    public static void main(String[] args) {
        printConfig(getGiftCoinfig());
        Map <Integer, Integer> result = new LinkedHashMap<>();
        LuckyUtil3 luckyUtil2 = new LuckyUtil3();
        ArrayList<Double> list = new ArrayList<>();
        int times = 10000;
        for (int i = 0; i < 1000; i++) {
            UserLuck user = new UserLuck();
            user.bloodBase = i%10==0 ? 1.15 : 1.05;
//            user.bloodBase = 1.15;
            while (true) {
                luckyUtil2.luckTest(user, result);
                if (user.pay>2000000 || user.income/user.pay>1.175d || user.pay*1.175d-user.income>100000) {
//                if (user.pay*1.17d-user.income>100000) {
                    break;
                }
            }
            double rate  = BigDecimal.valueOf(user.income).divide(BigDecimal.valueOf(user.pay), 4, BigDecimal.ROUND_HALF_UP).doubleValue();
            System.out.println(i+": 投入："+user.pay+" 产出:"+user.income +"  爆率:"+rate+ " 实际盈利："+(int)(user.income-user.pay*1.17));
        }
        printTj(list, times);

        result.keySet().stream().sorted().forEach(k->{
            System.out.println("抽到:"+k+"   次:"+result.get(k));
        });
    }

}


class UserLuck{
    int id;
    int pay;
    int income;
    double bloodBase;

    int totalPay;
    int totalIncome;
    int currPay;
    int currIncome;

}